package org.seamon.management.util.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.seamon.management.pojo.dto.LoginDTO;
import org.seamon.management.pojo.dto.ResetPwdDto;
import org.seamon.management.pojo.dto.UserInsertDto;
import org.seamon.management.pojo.entity.Dept;
import org.seamon.management.pojo.entity.Log;
import org.seamon.management.pojo.entity.User;
import org.seamon.management.service.DeptService;
import org.seamon.management.service.LogService;
import org.seamon.management.service.UserService;
import org.seamon.management.util.enums.StatusEnum;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private LogService logService;
    @Resource
    private UserService userService;
    @Resource
    private DeptService deptService;

    // 定义切点：拦截所有 controller 包下的方法
    @Pointcut("execution(* org.seamon.management.controller..*.*(..))")
    public void logPointCut() {
    }

    // 后置返回通知：记录成功日志
    @AfterReturning(pointcut = "logPointCut()")
    public void doAfterReturning(JoinPoint joinPoint) {
        Log sysLog = buildLog(joinPoint, null);
        sysLog.setStatus(StatusEnum.SUCCESS.getCode());
        logService.save(sysLog);
    }

    // 异常通知：记录失败日志
    @AfterThrowing(pointcut = "logPointCut()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        Log sysLog = buildLog(joinPoint, ex);
        sysLog.setStatus(StatusEnum.FILE.getCode());
        sysLog.setErrorInfo(ex.getMessage());
        logService.save(sysLog);
    }

    // 构建日志对象
    private Log buildLog(JoinPoint joinPoint, Throwable ex) {
        Log sysLog = new Log();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取方法上的注解
        org.seamon.management.util.aspect.Log annotation = method.getAnnotation(org.seamon.management.util.aspect.Log.class);
        if (annotation != null) {
            sysLog.setTitle(annotation.title());
            sysLog.setBusinessType(annotation.businessType().getCode());
        }

        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            try {
                // 密码脱敏
                sanitizePasswords(args);

                // 使用Jackson等工具序列化对象
                ObjectMapper objectMapper = new ObjectMapper();
                sysLog.setParams(objectMapper.writeValueAsString(args));
            } catch (JsonProcessingException e) {
                // 记录序列化失败的原始类名
                sysLog.setParams("SerializationFailed:" + Arrays.toString(args));
            }
        }

        sysLog.setMethodName(signature.getName());
        sysLog.setCreateTime(LocalDateTime.now());

        // 获取当前用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        if (username != null) {
            sysLog.setOperator(username);
        }
        User user = userService.getByUsername(username);
        if (user != null && user.getDeptId() != null) {
            Dept dept = deptService.getById(user.getDeptId());
            if (dept != null) {
                sysLog.setDeptName(dept.getName());
            }
        }

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String url = request.getRequestURL().toString();
        String requestMethod = request.getMethod();
        String userAgent = request.getHeader("User-Agent");
        String ip = getClientIP(request);

        sysLog.setRequestUrl(url);
        sysLog.setRequestMethod(requestMethod);
        sysLog.setUserAgent(userAgent);
        sysLog.setIp(ip);

        return sysLog;
    }

    private String getClientIP(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        } else if (ip.contains(",")) {
            // X-Forwarded-For 可能包含多个 IP，第一个是客户端真实 IP
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }

    private void sanitizePasswords(Object[] args) {
        for (Object arg : args) {
            if (arg == null) continue;

            if (arg instanceof ResetPwdDto ||
                    arg instanceof UserInsertDto ||
                    arg instanceof LoginDTO) {

                try {
                    Field[] fields = arg.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        if ("password".equalsIgnoreCase(field.getName())) {
                            field.setAccessible(true);
                            Object value = field.get(arg);
                            if (value instanceof String) {
                                field.set(arg, "******");
                            }
                        }
                    }
                } catch (Exception e) {
                    // 异常处理
                    log.error("未能清除密码: {}", e.getMessage());
                    e.fillInStackTrace();
                }
            }
        }
    }
}
